package cn.sc.summer.gateway.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.sc.summer.gateway.service.RouteService;
import com.alibaba.fastjson.JSONObject;
import cn.sc.summer.constant.gateway.GatewayConstant;
import cn.sc.summer.redis.util.RedisHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 类名：刷新路由
 *
 * @author a-xin
 * @date 2023/12/7 16:15
 */
@Slf4j
@Service
public class RouteServiceImpl implements RouteService, ApplicationEventPublisherAware {

    /**
     * 提供了对路由的增加删除等操作
     */
    @Resource
    private RouteDefinitionWriter routeDefinitionWriter;

    /**
     * 事件发布者,是ApplicationContext的父接口之一，他的功能就是发布事件，也就是把某个事件告诉所有与这个事件相关的监听器
     */
    private ApplicationEventPublisher publisher;

    @Override
    public void update(RouteDefinition routeDefinition) {
        log.warn("==> Update the routing configuration item: {}", routeDefinition);
        this.routeDefinitionWriter.delete(Mono.just(routeDefinition.getId()));
        routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
        List<String> predicateArgs = getPredicateArgs(routeDefinition);
        RedisHelper.hDel(GatewayConstant.ROUTING_PREFIX, routeDefinition.getId());
        RedisHelper.hSet(GatewayConstant.ROUTING_PREFIX, routeDefinition.getId(), String.join(",", predicateArgs));
        RedisHelper.hSet(GatewayConstant.SWAGGER_DYNAMIC_ROUTING, routeDefinition.getId(), JSONObject.toJSONString(routeDefinition));
    }

    @Override
    public void add(RouteDefinition routeDefinition) {
        log.info("==> Add the routing configuration item: {}", routeDefinition);
        routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
        List<String> predicateArgs = getPredicateArgs(routeDefinition);
        RedisHelper.hSet(GatewayConstant.ROUTING_PREFIX, routeDefinition.getId(), String.join(",", predicateArgs));
        RedisHelper.hSet(GatewayConstant.SWAGGER_DYNAMIC_ROUTING, routeDefinition.getId(), JSONObject.toJSONString(routeDefinition));
    }

    @Override
    public void del(RouteDefinition routeDefinition) {
        log.error("==> Deleted the routing configuration item: {}", routeDefinition);
        this.routeDefinitionWriter.delete(Mono.just(routeDefinition.getId()));
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
        RedisHelper.hDel(GatewayConstant.ROUTING_PREFIX, routeDefinition.getId());
        RedisHelper.hDel(GatewayConstant.SWAGGER_DYNAMIC_ROUTING, routeDefinition.getId());
    }

    /**
     * 获取所有路由信息
     *
     * @return 路由信息
     */
    @Override
    public List<RouteDefinition> getAllRoute() {
        Map<Object, Object> objectObjectMap = RedisHelper.hmGet(GatewayConstant.SWAGGER_DYNAMIC_ROUTING);
        List<RouteDefinition> routeDefinitions = new ArrayList<>();
        if (CollUtil.isNotEmpty(objectObjectMap)) {
            objectObjectMap.forEach((key, value) -> routeDefinitions.add(JSONObject.parseObject(value.toString(), RouteDefinition.class)));
        }
        return routeDefinitions;
    }

    @Override
    public void setApplicationEventPublisher(@NonNull ApplicationEventPublisher applicationEventPublisher) {
        this.publisher = applicationEventPublisher;
    }

    /**
     * 获取路由表达式信息
     *
     * @param routeDefinition 路由信息
     * @return 表达式集合
     */
    private List<String> getPredicateArgs(RouteDefinition routeDefinition) {
        List<PredicateDefinition> predicates = routeDefinition.getPredicates();
        if (CollUtil.isNotEmpty(predicates)) {
            return predicates.stream().map(predicate -> predicate.getArgs().get("pattern"))
                    .collect(Collectors.toList());
        }
        return new ArrayList<>();
    }

}


